#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <errno.h>

#define MAX_CLIENTS 1024
#define BUFFER_SIZE 4096

// 客户端连接结构体
typedef struct {
    int fd;                // 客户端文件描述符
    char rbuf[BUFFER_SIZE]; // 读缓冲区
    int rlen;              // 已读取数据长度
    char wbuf[BUFFER_SIZE]; // 写缓冲区
    int wlen;              // 待发送数据长度
} Client;

Client *clients[MAX_CLIENTS]; // 客户端连接池

int main() {
    int listenfd, connfd, maxfd, nready;
    fd_set allrset, allwset, rset, wset;
    struct sockaddr_in servaddr, cliaddr;
    socklen_t clilen;

    // 初始化客户端池
    for (int i = 0; i < MAX_CLIENTS; i++) {
        clients[i] = NULL;
    }

    // 创建监听套接字
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 20);

    // 初始化文件描述符集合
    FD_ZERO(&allrset);
    FD_ZERO(&allwset);
    FD_SET(listenfd, &allrset);
    maxfd = listenfd;

    printf("[Server] Listening on port 9999...\n");

    while (1) {
        rset = allrset;
        wset = allwset;
        struct timeval timeout = {5, 0}; // 5秒超时
        nready = select(maxfd + 1, &rset, &wset, NULL, &timeout);
        printf("[Event] Select returned %d\n", nready);

        // 处理新连接
        if (FD_ISSET(listenfd, &rset)) {
            clilen = sizeof(cliaddr);
            connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
            
            // 将新客户端加入连接池
            int i;
            for (i = 0; i < MAX_CLIENTS; i++) {
                if (clients[i] == NULL) {
                    Client *c = (Client *)malloc(sizeof(Client));
                    c->fd = connfd;
                    c->rlen = 0;
                    c->wlen = 0;
                    clients[i] = c;
                    FD_SET(connfd, &allrset);
                    if (connfd > maxfd) maxfd = connfd;
                    printf("[Event] New client fd=%d connected (total: %d)\n", 
                          connfd, i+1);
                    break;
                }
            }
            if (i == MAX_CLIENTS) {
                fprintf(stderr, "Too many clients\n");
                close(connfd);
            }
        }

        // 遍历所有可能的客户端
        for (int i = 0; i < MAX_CLIENTS; i++) {
            Client *c = clients[i];
            if (c == NULL) continue;

            int fd = c->fd;

            /* ---------- 处理读事件 ---------- */
            if (FD_ISSET(fd, &rset)) {
                int n = read(fd, c->rbuf + c->rlen, BUFFER_SIZE - c->rlen);
                if (n <= 0) {
                    // 连接关闭或错误
                    printf("[Event] Client fd=%d disconnected\n", fd);
                    close(fd);
                    FD_CLR(fd, &allrset);
                    FD_CLR(fd, &allwset);
                    free(c);
                    clients[i] = NULL;
                } else {
                    c->rlen += n;
                    printf("[Data] Recv %d bytes from fd=%d\n", n, fd);

                    // 示例逻辑：将接收数据原样加入写缓冲区
                    memcpy(c->wbuf + c->wlen, c->rbuf, n);
                    c->wlen += n;
                    FD_SET(fd, &allwset); // 注册写事件
                }
            }

            /* ---------- 处理写事件 ---------- */
            if (FD_ISSET(fd, &wset) && c->wlen > 0) {
                int sent = write(fd, c->wbuf, c->wlen);
                if (sent > 0) {
                    printf("[Data] Sent %d bytes to fd=%d\n", sent, fd);
                    // 移动剩余数据到缓冲区头部
                    memmove(c->wbuf, c->wbuf + sent, c->wlen - sent);
                    c->wlen -= sent;
                    if (c->wlen == 0) {
                        FD_CLR(fd, &allwset); // 无数据可写时取消监控
                    }
                } else {
                    perror("write error");
                }
            }
        } // end for
    } // end while

    return 0;
}